﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using VersionComparer.Base;
using VersionComparer.Comparer;
using VersionComparer.DataModel.Assemblies.Images;

namespace VersionComparer.DataModel.Assemblies
{
    [Serializable]
    [Order(2)]
    public class TypeItem : AssemblyItemBase<Type>
    {
        string typeNamespace;
        RtfBuilder rtf = new RtfBuilder ();
        bool isNested = false;

        protected override Type AssociatedType(Type value)
        {
            return value;
        }
        
        protected override void Initialize(Type value)
        {
            isNested = value.IsNested;
            IsSealed = value.IsSealed;
            IsPublic = value.IsPublic;
            IsStatic = value.IsSealed && value.IsAbstract;
            IsAbstract = value.IsAbstract;
            IsOverride = value.DeclaringType != value.ReflectedType;
            Name = UnderlyingTypeName;
            typeNamespace = value.Namespace;
            if (Shallow)
                Text = Name;
            else
                Text = string.Format("{0}{1}{2}", AccessorsAsCode, AddSpaceIfNotEmpty(TypeType), Name);
            base.Initialize(value);
        }

        protected override CustomAttributeData[] GetAttributes(Type value)
        {
            return CustomAttributeData.GetCustomAttributes(value).ToArray();
        }
        protected virtual bool InsertContentToFullText
        {
            get
            {
                return true;
            }
        }
       

        protected RtfBuilder Rtf
        {
            get
            {
                return rtf;
            }
        }
        public virtual string TypeType
        {
            get
            {
                return "class";
            }
        }
        protected override void BuildRtf(RtfBuilder builder, bool differencesOnly, bool useParentColor)
        {
            if (Shallow)
                builder.Append(rtf);
            else
            {
                RtfBuilder childsRft = new RtfBuilder();
               
                RtfBuilder attributesRtf = new RtfBuilder();

                var childs = SubItems;
                if (!differencesOnly || childs.Length == 0)
                    childs = GetChilds();
                if (childs != null && childs.Length > 0)
                {
                    var valueChilds = childs.OfType<AssemblyValueItem>();
                    foreach (AssemblyValueItem valueChild in valueChilds)
                        valueChild.GetAsRtf(builder, differencesOnly, useParentColor);
                    Type lastType = null;
                    foreach (ComparableItem child in childs)
                    {
                        if (!(child is AttributeItem))
                        {
                            var shallowType = child as TypeItem;
                            if (shallowType == null || !shallowType.Shallow)
                            {
                                if (child.GetType() != lastType)
                                    childsRft.AppendLine("");
                                lastType = child.GetType();
                                child.GetAsRtf(childsRft, differencesOnly, useParentColor);
                            }
                        }
                        else
                        {
                            child.GetAsRtf(attributesRtf, differencesOnly, useParentColor);
                        }
                    }

                    if (!isNested)
                    {
                        builder.Append("namespace ", keywordColor);
                        builder.AppendLine(typeNamespace);
                        builder.AppendLine("{");
                        builder.Ident();
                    }
                    builder.Append(attributesRtf);
                    builder.Append(rtf);
                    builder.AppendLine();
                    builder.AppendLine("{");
                    builder.Ident();
                    builder.Append ( childsRft );
                    if (!isNested)
                    {
                        builder.DeIdent();
                        builder.AppendLine("}");
                    }
                    builder.DeIdent();
                    builder.AppendLine("}");
                }
            }
        }
        protected override string GetFullText(Type value)
        {
            rtf = new RtfBuilder();
            if (Shallow)
            {
                
                BuildTypeRtf(rtf, value, true);
                return rtf.Text;
            }
            
           
            rtf.Append(string.Format ( "{0}{1} ", AccessorsAsCode , TypeType ), keywordColor);
            rtf.Append(Name, nameColor);

            #region inheritances
            Type baseType = value.BaseType;
            if (baseType == typeof(ValueType))
                baseType = null;
           

            bool firstInheritor = true;
            var interfaces = value.GetInterfaces();
            if (baseType != null || interfaces.Length > 0)
                rtf.Append(": ");
            if (baseType != null)
            {
                firstInheritor = false;
                BuildTypeRtf(rtf, baseType, true);
            }
            List<Type> alreadyDeclaredTypes = new List<Type>();
            if (baseType != null)
                alreadyDeclaredTypes.Add(baseType);
            foreach (var inheritedInterface in interfaces)
            {
                bool implicitlyIncluded = false;
                foreach (Type type in alreadyDeclaredTypes)
                {
                    if (type.GetInterface ( inheritedInterface.Name ) != null )
                    {
                        implicitlyIncluded = true;
                        break;
                    }

                }
                if (!implicitlyIncluded)
                {
                    alreadyDeclaredTypes.Add(inheritedInterface);
                    if (!firstInheritor)
                        rtf.Append(" , ");
                    firstInheritor = false;
                    BuildTypeRtf(rtf, inheritedInterface, true);
                }
            }
            #endregion inheritances

            var childs = GetChilds();
            StringBuilder header = new StringBuilder();
            StringBuilder builder = new StringBuilder();
            Type lastType = null;
            if (childs != null && childs.Length > 0)
            {
                foreach (ComparableItem child in childs)
                {
                    if (child is ValueItem)
                        header.AppendFormat("    //{0}\r\n", child.FullText);
                    else
                    {
                        var shallowType = child as TypeItem;
                        if (shallowType == null || !shallowType.Shallow)
                        {
                            if (child.GetType() != lastType)
                                builder.AppendLine();
                            lastType = child.GetType();
                            builder.AppendLine(Indent(child.FullText, 2));
                        }
                    }
                }
            }

            string result = string.Format(
@"
{0}
{1}
{2}
    }}
}}", header, rtf.Text , builder);

            return result;
        }
        protected override ComparableItem[] GetChilds(Type value)
        {
            Stopwatch s = new Stopwatch();
            s.Start();

            BindingFlags bindingFlags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
            List<ComparableItem> childs = new List<ComparableItem>();
            double d1 = s.ElapsedMilliseconds;
            if (!Shallow)
            {
                List<EventInfo> events = new List<EventInfo> (value.GetEvents(bindingFlags));
                //childs.Add(Create<TypeItem, Type>(value.BaseType, this, "BaseType"));
                
                /*
                foreach (Type type in value.GetInterfaces())
                    childs.Add(Create<TypeItem, Type>(type, this, "Interface"));
                */
                /*
                foreach (Type type in value.GetGenericArguments())
                    childs.Add(Create<TypeItem, Type>(type, this, "GenericArgument"));

                foreach (Attribute attribute in value.GetCustomAttributes(true))
                    childs.Add(Create<TypeItem, Type>(attribute.GetType(), this, "Attribute"));
                */
                foreach (Type nestedType in value.GetNestedTypes(bindingFlags))
                    childs.Add(CreateType(nestedType, this));
                List<ComparableItem> aux = new List<ComparableItem>();
                foreach (FieldInfo field in value.GetFields(bindingFlags))
                {
                    if (field.DeclaringType == field.ReflectedType && !field.Name.StartsWith("<"))
                    {
                        if (events.FirstOrDefault(x => x.Name == field.Name) == null)
                            aux.Add(Create<FieldItem, FieldInfo>(field, this));
                        else
                        {
                        }
                    }
                }
                aux.Sort((x, y) => x.Text.CompareTo(y.Text));
                childs.AddRange(aux);
                foreach (var constructor in value.GetConstructors(bindingFlags))
                {
                    if ( constructor.DeclaringType == constructor.ReflectedType )
                        childs.Add(Create<ConstructorItem, ConstructorInfo>(constructor, this));
                }
                aux = new List<ComparableItem>();
                foreach (PropertyInfo property in value.GetProperties(bindingFlags))
                {
                    if (property.DeclaringType == property.ReflectedType)
                        aux.Add(Create<PropertyItem, PropertyInfo>(property, this));
                }
                aux.Sort((x, y) => x.Text.CompareTo(y.Text));
                childs.AddRange(aux);
                aux = new List<ComparableItem> ();

                foreach (MethodInfo method in value.GetMethods(bindingFlags))
                {
                    try
                    {
                        if (method.DeclaringType == method.ReflectedType && !(method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_"))))
                        {
                            var parameters = method.GetParameters();
                            if (parameters.Length != 1 || (events.FirstOrDefault(x => x.EventHandlerType == parameters[0].ParameterType && (string.Format("add_{0}", x.Name) == method.Name || string.Format("remove_{0}", x.Name) == method.Name)) == null))
                            {
                                var newMethod = Create<MethodItem, MethodInfo>(method, this);
                                aux.Add(newMethod);
                            }
                        }
                    }
                    catch
                    { }
                }
                aux.Sort((x, y) => x.Text.CompareTo(y.Text));
                childs.AddRange(aux);
                foreach (EventInfo typeEvent in events)
                    childs.Add(Create<EventItem, EventInfo>(typeEvent, this));
            }

            
            return childs.ToArray();
        }

        protected override System.Drawing.Image GetSpecificImage()
        {
            return AssemblyResources._class;
        }

        public override double Matches(ComparisonAlgorithm algorithm, ComparableItem other)
        {
            return typeNamespace == (other as TypeItem).typeNamespace ? Match.CertainMatch : Match.NoMatch;
        }

        protected override int HashCodeForMatch
        {
            get
            {
                return typeNamespace.GetHashCode();
            }
        }

        protected override int CompareToItem(ComparableItem other)
        {
            return typeNamespace.CompareTo((other as TypeItem).typeNamespace);
        }

       
    }
}
